// Copyright 2009 Pigasus.
// GNU GPL v2.
// Author: John Zhang and Jordan Rupprecht.
//
// Defines the ThreadPool class.
//
// Remember: compile using -pthread when using threads.

#ifndef THREADPOOL_H_
#define THREADPOOL_H_

#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#include <map>
#include <queue>
#include <string>
#include <vector>

#include "thread.h"

using namespace std;

// Default thread pool size, i.e. max number of threads allowed to run
// concurrently.
const int kDefaultThreadPoolSize = 50;
// Default thread pool delay, i.e. number of seconds to wait in between checking
// to see if new threads can be executed.
const int kDefaultThreadPoolDelay = 1;

// A thread is not defined. 
const int kThreadStatusUndefined = 0;
// A thread has been added to a threadpool, but has not been run.
const int kThreadStatusQueued = 1;
// A thread is currently running.
const int kThreadStatusRunning = 2;
// A thread has finished running.
const int kThreadStatusDone = 3;
// A thread has just finished executing and is informing the threadpool before
// anything else.
const int kThreadStatusMarkAsDone = 4;

// Class representing thread pools. To prevent an excessive number of threads
// from being run concurrently, you shouldn't call Thread.Start() directly,
// instead, add it to a thread pool. The thread pool will ensure the number of
// threads being run (for each pool) is no more than a specified size. 
class ThreadPool {
  pthread_t thread;
  int type;
  pthread_mutex_t *mutex_threads_to_run;

  // Run the thread.
  static void *ThreadFunction(void *t) {
    ((ThreadPool*)t)->Run();
    return (void *)pthread_self();
  }

  // A queue containing a list of <id, Threads> that are not yet run.
  queue< pair<int, Thread*> > threads_to_run;
  // Maps thread ID's to threads which are already being run.
  map<int, Thread*> running_threads;
  // Maps thread ID's to their status.
  map<int, int> threads_status;  

  // Maximum number of threads to be allowed running concurrently in this pool.
  int size;
  // Number of seconds to wait in between updating the running threads.
  int delay;
  // The number of running threads at any given time.
  int num_running_threads;
  // The next thread ID to return. Basically a counter. Resets to zero when the
  // maximum size of int is reached.
  int next_thread_id;
  
  // Returns a new thread ID. Basically a counter.
  int GetNewThreadID();

 public:
  // Initializes all the class variables with default parameters.
  ThreadPool(int type_=kNullThreadType,
             int size_=kDefaultThreadPoolSize,
             int delay_=kDefaultThreadPoolDelay);

  // Empty destructor.
  ~ThreadPool();

  // Runs the thread pool. Will only stop once stop_when_done is set to true and
  // all the threads in the todo list are done.
  void Run();

  // Add a thread to the to do list.
  int AddThread(Thread *thread);

  // Set a status of a running or complete job. Will do nothing if another ID is
  // set.
  void SetStatus(int id, int status);

  // Returns the status of the thread with id.
  int GetStatus(int id);

  // Returns whether or not this threadpool has threads running or queued to
  // run.
  bool HasJobsRunning();

  // Start threadpool execution.
  int Start() {
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    int tid = pthread_create(&thread, NULL, ThreadPool::ThreadFunction,
                             (void*)this);

    pthread_attr_destroy(&attr);
    return tid;
  }

  // Wait until Run() finishes doing its thing.
  int Wait() {
    return pthread_join(thread, NULL);
  }
};

#endif  // THREADPOOL_H_
